package xorm

import (
	"context"
	"gitee.com/KotlinToGo/xorm/core"
	"gitee.com/KotlinToGo/xorm/dialect"
	"gitee.com/KotlinToGo/xorm/log"
	"gitee.com/KotlinToGo/xorm/mapper"
	"gitee.com/KotlinToGo/xorm/schema"
	"os"
	"runtime"
	"time"
)
// NewEngine new a db manager according to the parameter. Currently support four
// drivers
func NewEngine(driverName ,dsn string,tagKey ...string) (*Engine, error) {
	dialect, err := dialect.OpenDialect(driverName, dsn)
	if err != nil {
		return nil, err
	}
	db, err := core.Open(driverName, dsn)
	if err != nil {
		return nil, err
	}
	return newEngine(driverName, dsn, dialect, db,tagKey...)
}

func newEngine(driver, dsn string, dial dialect.IDialect, db *core.DB,tagKey ...string) (*Engine, error) {

	cache := schema.NewManager() // 缓存器
	mapper := mapper.NewCacheMapper(new(mapper.SnakeMapper))// 缓存映射器，默认是 SnakeMapper
	var tagParser *dialect.Parser
	if len(tagKey) == 0 {
		tagParser = dialect.NewParser("db", dial, mapper, mapper, cache) // tag解析器
	}else {
		tagParser = dialect.NewParser(tagKey[0], dial, mapper, mapper, cache) // tag解析器
	}

	engine := &Engine{
		dialect:        dial,
		TZLocation:     time.Local,
		ctx: context.Background(),
		cache:      cache,
		tagParser:      tagParser,
		driver:     driver,
		dsn: dsn,
		db:             db,
		logSessionID:   false,
	}

	if dial.URI().DBType == schema.Sqlite {
		engine.DatabaseTZ = time.UTC
	} else {
		engine.DatabaseTZ = time.Local
	}

	logger := log.NewSimpleLogger(os.Stdout)
	logger.SetLevel(log.LoggerInfo)
	engine.SetLogger(log.NewLoggerAdapter(logger))

	runtime.SetFinalizer(engine, func(engine *Engine) {
		_ = engine.Close()
	})

	return engine, nil
}
/*
// NewEngine 返回一个 *Engine 对象，一个APP可以同时存在多个Engine引擎，一个Engine只对应一个数据库。
func NewEngine(driver,dsn string,tagKey ...string) (engine *Engine, err error) {
	o.Do(
		func() {
			dial, dErr := dialect.OpenDialect(driver,dsn) // 获取一个 dialect
			if dErr != nil {
				err = dErr
			}
			db, dbErr := core.Open(driver,dsn)
			if dbErr != nil {
				err = dbErr
			}
			cache := schema.NewManager()
			mapper := mapper.NewCacheMapper(new(mapper.SnakeMapper))
			var tagParser *dialect.Parser
			if len(tagKey) == 0 {
				tagParser = dialect.NewParser("xorm", dial, mapper, mapper, cache) // tag解析器
			}else {
				tagParser = dialect.NewParser(tagKey[0], dial, mapper, mapper, cache) // tag解析器
			}
			// 初始化一个Engine实例
			e := &Engine{
				db:             db,
				dialect:        dial,
				cache:      cache,
				TZLocation:     time.Local,
				ctx: context.Background(),
				tagParser:      tagParser,
				driver:     driver,
				dsn: dsn,
				logSessionID:   false,
			}
			// 设置数据库时区。
			if dial.URI().DBType == schema.Sqlite {
				e.DatabaseTZ = time.UTC
			}else {
				e.DatabaseTZ = time.Local
			}

			// 设置日志
			l:= log.NewSimpleLogger(os.Stdout)
			l.SetLevel(log.LoggerInfo)
			e.SetLogger(log.NewLoggerAdapter(l))

			runtime.SetFinalizer(e, func(e *Engine) {
				_ = e.Close()
			})
			engine = e
		},
	)
	return
}*/


// NewEngineWithParams new a db manager with params. The params will be passed to dialects.
func NewEngineWithParams(driverName string, dataSourceName string, params map[string]string) (*Engine, error) {
	engine, err := NewEngine(driverName, dataSourceName)
	engine.dialect.SetParams(params)
	return engine, err
}

// NewEngineWithDialectAndDB new a db manager according to the parameter.
// If you do not want to use your own dialect or db, please use NewEngine.
// For creating dialect, you can call dialects.OpenDialect. And, for creating db,
// you can call core.Open or core.FromDB.
func NewEngineWithDialectAndDB(driverName, dataSourceName string, dialect dialect.IDialect, db *core.DB) (*Engine, error) {
	return newEngine(driverName, dataSourceName, dialect, db)
}

// Clone clone an engine
func (e *Engine) Clone() (*Engine, error) {
	return NewEngine(e.GetDriver(), e.GetDSN())
}
